﻿using System;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Security;
using Stimulus360.Captcha;
using Stimulus360.Models.Data;
using Stimulus360.Services;
using Stimulus360.Services.Interfaces;

namespace Stimulus360.Controllers
{

    [HandleError]
    public class AccountController : Controller
    {
        /// <summary>
        ///     Instance of UserProfileService
        /// </summary>
        private readonly IUserService _userService;

        ///// <summary>
        /////     The default image type is set it to png
        ///// </summary>
        //private static readonly string DefaultImageType = Resources.Stimulus360.ImageType;


        /// <summary>
        ///     Instance of InvestmentCategoryService
        /// </summary>
        private readonly IInvestmentCategoryService _investmentCategoryService;

        /// <summary>
        ///     Default Constructor. Instantiates the services
        /// </summary>
        public AccountController()
            : this(new FormsAuthenticationService(), new MembershipService(),
                    new UserService(), new InvestmentCategoryService())
        {
        }

        /// <summary>
        ///     Overloaded constructor to allow Dependency Injection.
        /// </summary>
        /// <param name="formsAuth">
        ///     Instance of FormAuthentication
        /// </param>
        /// <param name="service">
        ///     Membership Services instance
        /// </param>
        /// <param name="userService">
        ///     Instance of user service
        /// </param>
        /// <param name="investmentCategoryService">
        ///     Instance of Investment Category
        /// </param>
        public AccountController(IFormsAuthentication formsAuth, IMembershipService service,
            IUserService userService, IInvestmentCategoryService investmentCategoryService)
        {
            FormsAuth = formsAuth ?? new FormsAuthenticationService();
            MembershipService = service ?? new MembershipService();
            _userService = userService ?? new UserService();
            _investmentCategoryService = investmentCategoryService ?? new InvestmentCategoryService();
        }

        /// <summary>
        ///     Returns the Forms authentication instance
        /// </summary>
        public IFormsAuthentication FormsAuth
        {
            get;
            private set;
        }

        /// <summary>
        ///     Returns the Membership service instance
        /// </summary>
        public IMembershipService MembershipService
        {
            get;
            private set;
        }

        /// <summary>
        ///     Action Method to render the Login screen.
        /// </summary>
        /// <returns>
        ///     The Log on view.
        /// </returns>
        public ActionResult LogOn()
        {
            return View();
        }

        /// <summary>
        ///     method allows user to logged into the system and generate the ASP.NET Form Aunthentication Cookies.
        /// </summary>
        /// <param name="userName">
        ///     Username/Community Alias of an user
        /// </param>
        /// <param name="password">
        ///     Password
        /// </param>
        /// <param name="rememberMe">
        ///     Boolean value set it True if required to create persisted cookies else set as false (default "false")
        /// </param>
        /// <returns>
        ///     Login View with error message if error is generated, else user will redirect to the URL specified in returnUrl param
        /// </returns>
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult LogOn(string userName, string password, bool rememberMe)
        {
            if (!ValidateLogOn(userName, password))
            {
                ViewData["rememberMe"] = rememberMe;
                return View();
            }

            FormsAuth.SignIn(userName, rememberMe);
            return Redirect(FormsAuth.Redirect(userName, rememberMe));
        }

        /// <summary>
        ///     Method allows user to logout of the system 
        /// </summary>
        /// <returns>
        ///     Home View
        /// </returns>
        public ActionResult LogOff()
        {

            FormsAuth.SignOut();
            return RedirectToAction("Index", "Home");

        }

        /// <summary>
        ///     Method renders the Register view
        /// </summary>
        /// <returns>
        ///     Register View
        /// </returns>
        public ActionResult Register()
        {
            NewUserViewModel model = new NewUserViewModel
            {
                MinPasswordLength = MembershipService.MinPasswordLength,
                User =
                    new UserProfile
                    {
                        DateCreated = DateTime.Now,
                        UserName = "",
                        Email = "",
                        FirstName = ""
                    },
                InvestmentCategories = _investmentCategoryService.GetAll()
            };
            RemoveSession("userImage");
            return View(model);
        }

        /// <summary>
        ///     This method cancels the registration by removing session       
        /// </summary>
        public void CancelRegister()
        {
            RemoveSession("userImage");
        }

        /// <summary>
        ///     Method deletes an item from the session-state collection.
        /// </summary>
        /// <param name="sessionName"></param>
        private void RemoveSession(string sessionName)
        {
            if (HttpContext.Session[sessionName] != null)
                HttpContext.Session.Remove(sessionName);
        }

        /// <summary>
        ///     This method displays the selected image during group registration
        /// </summary>
        public void PreviewImage()
        {
            if (Request.Files["userFileUpload"] == null)
                return;

            var userImage = Request.Files["userFileUpload"];
            var userImageData = new byte[userImage.InputStream.Length];

            try
            {
                userImage.InputStream.Read(userImageData, 0, userImageData.Length);

                
                if (HttpContext.Session["userImage"] != null)
                    HttpContext.Session.Remove("userImage");

                HttpContext.Session.Add("userImage", userImageData);
            }
            finally
            {
                userImage.InputStream.Close();
            }
        }

        /// <summary>
        ///     Method displays the default image of an user
        /// </summary>
        /// <returns>
        ///     FilePath Result to render the user image on screen
        /// </returns>
        public ActionResult NewUserImage()
        {
            var filename = Server.MapPath("~/") + Resources.Stimulus360.DefaultImage;
            try
            {
                if (HttpContext.Session["userImage"] != null)
                {
                    var image = (byte[])HttpContext.Session["userImage"];
                    if (image == null)
                    {
                        return File(filename, Resources.Stimulus360.ImageType);
                    }
                    return File(image, Resources.Stimulus360.ImageType);
                }
            }
            catch (InvalidCastException)
            { filename = Server.MapPath("~/") + Resources.Stimulus360.DefaultImage; }
            return File(filename, Resources.Stimulus360.ImageType);
        }


        /// <summary>
        ///     This method used to show the image from the memory (showing group image on group register page.)
        /// </summary>
        /// <returns></returns>
        public ActionResult Temp()
        {
            var filename = Server.MapPath("~/") + Resources.Stimulus360.DefaultImage;

            try
            {
                //SessionRow session = new SessionRow("userImage", "stimulus360");
                
                if (HttpContext.Session["userImage"] != null)
                {
                    var userImageData = (byte[])HttpContext.Session["userImage"];

                    if (userImageData != null)
                        return File(userImageData, Resources.Stimulus360.ImageType);
                }
            }
            catch (InvalidCastException)
            {
                filename = Server.MapPath("~/") + Resources.Stimulus360.DefaultImage;
            }

            return File(filename, Resources.Stimulus360.ImageType);
        }

        /// <summary>
        ///     Method allows user to Join/register the application by providing their details
        /// </summary>
        /// <param name="captchaValid">
        ///     True: valid captchaText
        ///     False: Invalid captchaText
        /// </param>
        /// <param name="interests">
        ///     String having comma seperated interest names and associated interset id's
        /// </param>
        /// <returns>
        ///     View model for new user.
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "captcha", Justification = "captcha is a valid term"), ValidateAntiForgeryToken]
        [AcceptVerbs(HttpVerbs.Post)]
        [CaptchaValidation("captcha")]
        public ActionResult Register(bool captchaValid, string interests)
        {
            UserProfile user = new UserProfile();
            UpdateModel(user);
            user.CaptchaValid = captchaValid;

            if (HttpContext.Session["userImage"] != null)
                user.ThumbnailPhoto = (byte[])HttpContext.Session["userImage"];

            if (!user.IsValid)
            {
                ModelState.AddModelError(user.GetRuleViolations());
            }
            else
            {
                user.DateCreated = DateTime.Now;
                user.IdentityProviderUserId = Guid.NewGuid();

                // Attempt to register the user
                MembershipCreateStatus createStatus = _userService.Create(user, interests);

                if (createStatus == MembershipCreateStatus.Success)
                {
                    FormsAuth.SignIn(user.UserName, false /* createPersistentCookie */);
                    if (HttpContext.Session["userImage"] != null)
                        HttpContext.Session.Remove("userImage");

                    return RedirectToAction("Index", "Users");
                }
                ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
            }

            // If we got this far, something failed, redisplay form
            NewUserViewModel model = new NewUserViewModel
            {
                MinPasswordLength = MembershipService.MinPasswordLength,
                User = user,
                InvestmentCategories = _investmentCategoryService.GetAll(),
                CategoryIds = GetCategoryIds(interests)
            };
            return View(model);

        }

        /// <summary>
        ///     Method will get the user's selected interest with categor id and returns a string of category ids.
        ///     Method is used to show the user interest selection on join page
        /// </summary>
        /// <param name="interests">
        ///     String having comma seperated interest names and associated interset id's
        /// </param>
        /// <returns>
        ///     String having pipe "|" seperated interest ids.
        /// </returns>
        private static string GetCategoryIds(string interests)
        {
            StringBuilder categoryIds = new StringBuilder();
            var interestCategories = string.IsNullOrEmpty(interests) ? null : interests.TrimEnd(',').Split('?').Distinct().ToArray();
            if (interestCategories != null && interestCategories.Length == 2)
            {
                var arrInterestsIds = string.IsNullOrEmpty(interestCategories[1]) ? null : interestCategories[1].TrimEnd(',').Split(',').Distinct().ToArray();
                if (arrInterestsIds != null)
                {
                    for (int intLoop = 0; intLoop < arrInterestsIds.Length; intLoop++)
                    {
                        categoryIds.Append(arrInterestsIds[intLoop]);
                        categoryIds.Append("|");
                    }
                }
            }
            return categoryIds.ToString();
        }

        /// <summary>
        ///     Method called before the action method is invoked.
        /// </summary>
        /// <param name="filterContext">
        ///     Contains information about the current request and action.
        /// </param>
        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var action = (string)filterContext.RouteData.Values["action"];

            var httpMethod = filterContext.HttpContext.Request.HttpMethod;

            if (!(action == "Register" && httpMethod == "POST"))
                return;


            // make sure no values are getting sent in from the outside   
            if (filterContext.ActionParameters.ContainsKey("captchaValid"))
                filterContext.ActionParameters["captchaValid"] = null;

            // get the guid from the post back   
            string guid = filterContext.HttpContext.Request.Form["captcha-guid"];

            // check for the guid because it is required from the rest of the opperation   
            if (String.IsNullOrEmpty(guid))
            {
                filterContext.RouteData.Values.Add("captchaValid", false);
                return;
            }

            // get values   
            CaptchaImage image = CaptchaImage.GetCachedCaptcha(guid);
            string actualValue = filterContext.HttpContext.Request.Form["captcha"];
            string expectedValue = image == null ? String.Empty : image.Text;

            // removes the captch from cache so it cannot be used again   
            filterContext.HttpContext.Cache.Remove(guid);

            // validate the captch   
            filterContext.ActionParameters["captchaValid"] =
                !String.IsNullOrEmpty(actualValue)
                && !String.IsNullOrEmpty(expectedValue)
                && String.Equals(actualValue, expectedValue, StringComparison.OrdinalIgnoreCase);

            base.OnActionExecuting(filterContext);
        }

        #region Validation Methods

        /// <summary>
        ///     Method validate the input's by user and add the errors in ModelState
        /// </summary>
        /// <param name="userName">
        ///     Username/Community Alias
        /// </param>
        /// <param name="password">
        ///     Password
        /// </param>
        /// <returns>
        ///     Returns true if ther is no error, otherwise false
        /// </returns>
        private bool ValidateLogOn(string userName, string password)
        {

            if (String.IsNullOrEmpty(userName))
            {
                ModelState.AddModelError("username", Resources.Stimulus360.ValidationMessage_UserName);
            }
            if (String.IsNullOrEmpty(password))
            {
                ModelState.AddModelError("password", Resources.Stimulus360.ValidationMessage_Password);
            }

            if (!MembershipService.ValidateUser(userName, password))
            {
                ModelState.AddModelError("_FORM", Resources.Stimulus360.ValidationMessage_LoginForm);
            }
            return ModelState.IsValid;
        }


        /// <summary>
        ///     Method returs the descriptive text of membership create status
        /// </summary>
        /// <param name="createStatus">
        ///     Instance of Membership create status
        /// </param>
        /// <returns>
        ///     String represent the error status code
        /// </returns>
        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return Resources.Stimulus360.DuplicateUserName;

                case MembershipCreateStatus.DuplicateEmail:
                    return Resources.Stimulus360.DuplicateEmail;

                case MembershipCreateStatus.InvalidPassword:
                    return Resources.Stimulus360.InvalidPassword;

                case MembershipCreateStatus.InvalidEmail:
                    return Resources.Stimulus360.InvalidEmail;

                case MembershipCreateStatus.InvalidAnswer:
                    return Resources.Stimulus360.InvalidAnswer;

                case MembershipCreateStatus.InvalidQuestion:
                    return Resources.Stimulus360.InvalidQuestion;

                case MembershipCreateStatus.InvalidUserName:
                    return Resources.Stimulus360.InvalidUserName;

                case MembershipCreateStatus.ProviderError:
                    return Resources.Stimulus360.ProviderError;

                case MembershipCreateStatus.UserRejected:
                    return Resources.Stimulus360.UserRejected;

                default:
                    return Resources.Stimulus360.Default;
            }
        }
        #endregion

    }

}
